﻿using iTextSharp.text.pdf;
using iTextSharp.text.pdf.parser;
using KuaJing.Customs.Domain;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace KuaJing.Customs.Service
{
    public class DeclarationParser
    {
        private ItemParser itemParser = new ItemParser();
        private const char RowToken = '\n';

        public Declaration Parse(string fileName)
        {
            using (PdfReader reader = new PdfReader(fileName))
            {
                PdfReaderContentParser parser = new PdfReaderContentParser(reader);

                ITextExtractionStrategy strategy;
                Declaration declaration = null;
              var  items = new HashSet<DeclarationItem>();
                for (int i = 0; i < reader.NumberOfPages; i++)
                {
                    strategy = parser.ProcessContent<SimpleTextExtractionStrategy>(i + 1, new SimpleTextExtractionStrategy());
                    var result = strategy.GetResultantText();
                    declaration = Parse(result, items);

                }
                declaration.Items = items;
                return declaration;
            }
        }

        public Declaration Parse(string context, ISet<DeclarationItem> itmes)
        {
            foreach (var item in itemParser.Parse(ref context))
            {
                itmes.Add(item);
            }
            Declaration declaration = new Declaration();
            ParseApplicationUnit(declaration, context.Substring(0, context.IndexOf("中华人民共和国海关出口货物报关单") - 1));
            context = SkipRow(context.Substring(context.IndexOf("中华人民共和国海关出口货物报关单")), 11);
            List<string> proertyStringArray = context.Split(RowToken).ToList();
            foreach (var parse in propertyParseAction)
            {
                parse(proertyStringArray, declaration);
            }
            proertyStringArray = proertyStringArray.Where(s => !string.IsNullOrEmpty(s)).ToList();
            if (proertyStringArray.Count == 3)
            {
                declaration.ApplicationUnit = proertyStringArray[1];
                declaration.ApplicationUnitNumber = proertyStringArray[2];
            }
            return declaration;
        }
        private Action<List<string>, Declaration>[] propertyParseAction =
            new Action<List<string>, Declaration>[] {
                ParseNumbers,ParseExportPort,ParseDate,ParseUnits,
                ParseTransport,ParseTrade,
                ParseDestination,ParseFee,ParseContract,ParseContainer
    };
        private static void ParseContainer(List<string> context, Declaration declaration)
        {
            if (context.Count > 1)
            {
                //解决没有集装箱号  MHT1600015
                declaration.ContainerNumber = context[0].Trim();
                if (!string.IsNullOrEmpty(declaration.ContainerNumber))
                {
                    if (declaration.ContainerNumber.Length > 30)
                    {
                        declaration.ContainerNumber = null;
                        declaration.MarkAndRemark = context[0];
                    }
                }
                context.RemoveAt(0);
            }
        }
        private static void ParseContract(List<string> context, Declaration declaration)
        {
            string[] stringArray = Split(context[0]);
            declaration.ContractNumber = stringArray[0];
            if (NumberRegex.IsMatch(stringArray[1]))
            {
                //\JKJ15000215E
                declaration.GoodsNumber = int.Parse(stringArray[1]);
                if (stringArray.Length < 5)
                {
                    //新报关单
                    stringArray = stringArray.Concat(Split(context[1])).ToArray();
                    context.RemoveAt(1);
                }
                declaration.PackageType = stringArray[2];
                if (stringArray.Length < 5)
                {
                    //新报关单
                    stringArray = stringArray.Concat(Split(context[1])).ToArray();
                    context.RemoveAt(1);
                }
                declaration.GrossWeight = decimal.Parse(stringArray[3]);
                declaration.NetWeight = decimal.Parse(stringArray[4]);
            }
            context.RemoveAt(0);
        }

        private static readonly string[] TransactionModels = new string[] { "FOB", "C&F", "CIF" };
        private static void ParseFee(List<string> context, Declaration declaration)
        {
            //可能有风险，保费，运费都没有测试过
            int transationModelIndex = context.FindIndex(c => TransactionModels.Contains(c.Trim()));
            List<string> stringArray = new List<string>();
            for (int i = 0; i <= transationModelIndex; i++)
            {
                stringArray.AddRange(Split(context[0].Trim()));
                context.RemoveAt(0);
            }
            //\JKJ16002287E  JKJ16000743E
            stringArray = stringArray.Where(s => !string.IsNullOrEmpty(s) && s != "总价").ToList();
            if (stringArray.Count == 1)
            {
                stringArray.InsertRange(0, new string[] { null, null, null, null });
            }
            else if (stringArray.Count == 3)
            {
                //\JKJ15090032E
                stringArray.InsertRange(0, new string[] { null, null });
            }
            //else if (stringArray.Count == 4)
            //{
            //    //\JKJ16000743E
            //    stringArray.RemoveAt(0);
            //    stringArray.Insert(2, null);
            //    stringArray.Insert(2, null);
            //}
            if (stringArray[0] != null && !NumberRegex.IsMatch(stringArray[0]))
            {
                stringArray.Insert(0, stringArray[1]);
                stringArray.RemoveAt(2);
                //\JKJ16000076E
                stringArray[0] = stringArray[0].TrimEnd('%');

            }
            declaration.IncidentalFee = string.IsNullOrEmpty(stringArray[0]) ? default(decimal?) : decimal.Parse(stringArray[0]);
            if (stringArray[2] != null && !NumberRegex.IsMatch(stringArray[2]))
            {
                //\JKJ15090016E
                stringArray.Insert(2, stringArray[3]);
                stringArray.RemoveAt(4);
            }

            declaration.TransportFee = string.IsNullOrEmpty(stringArray[2]) ? default(decimal?) : decimal.Parse(stringArray[2]);
            declaration.TransactionModel = stringArray[4].Trim();
        }
        private static readonly Regex NumberRegex = new Regex("^[1-9][0-9]*$");
        private static void ParseDestination(List<string> context, Declaration declaration)
        {
            string[] stringArray = Split(context[0]);
            stringArray = stringArray.Where(s => !new string[] { "", "(", ")" }.Contains(s)).ToArray();

            declaration.DestinationlCountry = stringArray[0];
            declaration.DestinationCountryNumber = stringArray[1];


            if (stringArray.Length == 5)
            {
                var tmpArray = stringArray.ToList();
                tmpArray.Insert(3, string.Empty);
                stringArray = tmpArray.ToArray();
            }
            if (stringArray.Length != 6)
            {
                throw new NotSupportedException();
            }
            if (!string.IsNullOrEmpty(stringArray[3]) && !NumberRegex.IsMatch(stringArray[3]))
            {
                throw new NotSupportedException();
            }
            declaration.DestinationPort = stringArray[2].Trim();
            declaration.DestinationPortNumber = stringArray[3].Trim();

            declaration.DomesticLocation = stringArray[4].Trim();
            declaration.DomesticLocationNumber = stringArray[5].Trim();

            context.RemoveAt(0);
        }
        private static readonly Regex[] InvalidValues = new Regex[] { new Regex("[ ]*[(][ ]*[)][ ]*") ,
        new Regex("  ")};
        private static string[] Split(string context)
        {
            foreach (var invalidValue in InvalidValues)
            {
                context = invalidValue.Replace(context, " ");
            }
            return context.Split(' ');
        }
        private static void ParseTrade(List<string> context, Declaration declaration)
        {
            string[] stringArray = Split(context[0]);

            if (stringArray.Length == 3)
            {
                context.RemoveAt(0);
            }
            else if (stringArray.Length == 7 || stringArray.Length == 6)
            {
                //\JKJ15000215E length=7
                //\JKJ16002980E length=6
                stringArray = stringArray.Where(s => !new string[] { "", "(", ")" }.Contains(s)).ToArray();
                // stringArray = new string[] { stringArray[0], stringArray[1], stringArray[6] };
                context.RemoveAt(0);
            }
            else
            {
                //MHT1600015
                stringArray = new string[3];
                //throw new NotImplementedException();

            }
            if (stringArray.Length == 4)
            {
                declaration.NatureOfExemption = stringArray[0];
                declaration.NatureOfExemptionNumber = stringArray[1];
                declaration.TradeModel = stringArray[2];
                declaration.TradeModelNumber = stringArray[3];
            }
            else
            {
                declaration.SettlementWay = stringArray[0];
                declaration.TradeModel = stringArray[1];
                declaration.TradeModelNumber = stringArray[2];
            }
        }
        private static void ParseTransport(List<string> context, Declaration declaration)
        {
            string[] stringArray = Split(context[0]);

            declaration.TransportModel = stringArray[0];
            if (stringArray.Length == 2)
            {
                //\JKJ15090010E
                declaration.DeliveryNumber = stringArray[1];
                context.RemoveAt(0);
            }
            else if (stringArray.Length == 1)
            {
                throw new NotImplementedException();

            }
            else
            {
                int nextIndex = stringArray.ToList().FindIndex(s => s == "电汇");
                if (nextIndex > 0)
                {
                    //JKJ15090012E  没有提运单号
                    declaration.TransportName = string.Join(" ", stringArray.Skip(1).Take(nextIndex - 1));
                    context[0] = string.Join(" ", stringArray.Skip(nextIndex));
                }
                else
                {
                    declaration.TransportName = string.Join(" ", stringArray.Skip(1).Take(stringArray.Length == 3 ? 1 : 2));
                    declaration.DeliveryNumber = string.Join(" ", stringArray.Skip(stringArray.Length == 3 ? 2 : 3));

                    //JKJ16002561E 
                    //\JKJ15000035E
                    context.RemoveAt(0);

                }
            }
        }

        private static readonly Regex UnitNumberRegex = new Regex("^[0-9|NO]+[A-Z|0-9]*(-[0-9])?");
        private static void ParseUnits(List<string> context, Declaration declaration)
        {
            declaration.BusinessUnit = context[0].Trim();
            context.RemoveAt(0);
            declaration.BusinessUnitNumber = context[0].Trim();
            context.RemoveAt(0);
            declaration.ForwardingUnit = context[0].Trim();
            context.RemoveAt(0);
            var match = UnitNumberRegex.Match(context[0].Trim());
            if (match.Success)
            {
                //\JKJ15000215E
                declaration.ForwardingUnitNumber = match.Value;
                context[0] = context[0].Replace(match.Value, string.Empty).Trim();
                //JKJ16002164E
                if (string.IsNullOrEmpty(context[0]))
                {
                    context.RemoveAt(0);
                }
            }
        }
        private static void ParseDate(List<string> context, Declaration declaration)
        {
            string[] dateArray = Split(context[0]);
            declaration.ExportDate = DateTime.Parse(dateArray[0]);
            declaration.DeclarationDate = DateTime.Parse(dateArray[1]);
            context.RemoveAt(0);
        }
        private static void ParseNumbers(List<string> context, Declaration declaration)
        {
            string[] numberArray = Split(context[0]);
            declaration.PreInputNumber = numberArray[0];
            declaration.Code = numberArray[1];
            context.RemoveAt(0);
        }
        private static void ParseExportPort(List<string> context, Declaration declaration)
        {
            string[] stringArray = Split(context[0]);
            declaration.ExportPortNumber = stringArray[0];
            if (stringArray.Length == 2)
            { //JKJ16002164E
                declaration.ExportPort = stringArray[1];
            }
            context.RemoveAt(0);
        }
        private string SkipRow(string context, int rowCount)
        {
            for (int i = 0; i < rowCount; i++)
            {
                context = context.Substring(context.IndexOf(RowToken) + 1);
            }
            return context;
        }
        private void ParseApplicationUnit(Declaration declaration, string context)
        {
            declaration.ApplicationUnitNumber = context.Substring(context.LastIndexOf(RowToken) + 1);

            context = context.Substring(0, context.LastIndexOf(RowToken));
            declaration.ApplicationUnit = context.Substring(context.LastIndexOf(RowToken) + 1);

        }

        public class ItemParser
        {
            private static readonly string[] EOF = new string[] { "税费征收情况", "海关批注及签章" };
            private const string BOF = "用途：";
            public IEnumerable<DeclarationItem> Parse(ref string context)
            {
                ISet<DeclarationItem> items = new HashSet<DeclarationItem>();
                while (true)
                {
                    int rowEofIndex = context.IndexOf(BOF, BOF.Length);
                    if (rowEofIndex < 0)
                    {
                        foreach (var eof in EOF)
                        {
                            rowEofIndex = context.IndexOf(eof);
                            if (rowEofIndex > -1)
                            {
                                break;
                            }
                        }
                    }
                    else if (context.Substring(0, rowEofIndex - 1).Count(s => s == '\n') == 5)
                    {
                        //\JKJ16000013E
                        rowEofIndex = context.IndexOf(BOF, rowEofIndex + 1);
                        if (rowEofIndex < 0)
                        {
                            foreach (var eof in EOF)
                            {
                                rowEofIndex = context.IndexOf(eof);
                                if (rowEofIndex > -1)
                                {
                                    break;
                                }
                            }
                        }
                    }
                    items.Add(ParseItem(context.Substring(0, rowEofIndex - 1)));
                    context = context.Substring(rowEofIndex);
                    foreach (var eof in EOF)
                    {
                        if (context.StartsWith(eof))
                        {
                            return items;
                        }
                    }
                }
            }
            private DeclarationItem ParseItem(string context)
            {
                context = context.Trim().Trim(RowToken);
                List<string> itemProertyStringArray = context.Split(RowToken).ToList();
                DeclarationItem item = new DeclarationItem();
                foreach (var parse in propertyParseAction)
                {
                    parse(itemProertyStringArray, item);
                }
                return item;
            }
            private Action<List<string>, DeclarationItem>[] propertyParseAction = new Action<List<string>, DeclarationItem>[] {
                ParsePurpose,ParseQuantityAndUnit,                    ParseUnkown,//从前向后
                    ParseItemNumberAndGoodsNumber,ParseCurrency,ParsPriceToTax,ParseDestinationCountryNumber,ParseDestinationCountry,ParseGoodsName
                };
            /// <summary>
            /// 作用  
            /// </summary>
            /// <param name="context"></param>
            /// <param name="item"></param>
            /// <returns></returns>
            private static void ParsePurpose(List<string> context, DeclarationItem item)
            {
                //用途：
                //用途都为空，故存在风险
                if (context[0].Contains(BOF))
                {
                    context.RemoveAt(0);
                }
            }
            private static readonly Regex UnkownRegex = new Regex("[(|（][ ]*[0]?[ ]*[)|）]");
            private static void ParseUnkown(List<string> context, DeclarationItem item)
            {
                if (UnkownRegex.Match(context[0]).Success)
                {
                    context.RemoveAt(0);
                }
            }
            private static Regex QuantityAndUnitRegex =
                new Regex(@"(^[1-9]\d*\.\d*|0\.\d*)([\u4e00-\u9fa5]*)");
            private static void ParseQuantityAndUnit(List<string> context, DeclarationItem item)
            {
                //1820.0000个 第一计量单位
                //0.0000  第二计量单位
                //1820.0000个 申报


                var match = QuantityAndUnitRegex.Match(context[0]);
                item.FirstGoodsQuantity = decimal.Parse(match.Groups[1].Value);
                item.FirstQuantityUnit = match.Groups[2].Value;
                context.RemoveAt(0);

                match = QuantityAndUnitRegex.Match(context[0]);
                if (match.Success)
                {       //OC20150908

                    item.SecondGoodsQuantity = decimal.Parse(match.Groups[1].Value);
                    if (match.Groups.Count == 3)
                    {
                        item.SecondQuantityUnit = match.Groups[2].Value;
                    }
                    context.RemoveAt(0);
                }
                else
                {
                    item.SecondGoodsQuantity = item.FirstGoodsQuantity;
                }


                match = QuantityAndUnitRegex.Match(context[0]);
                if (match.Success)
                {
                    item.GoodsQuantity = decimal.Parse(match.Groups[1].Value);
                    item.QuantityUnit = match.Groups[2].Value;
                    context.RemoveAt(0);
                }
                else
                {
                    item.GoodsQuantity = item.FirstGoodsQuantity;
                    item.QuantityUnit = item.FirstQuantityUnit;
                }

            }
            private static void ParseItemNumberAndGoodsNumber(List<string> context, DeclarationItem item)
            {
                string[] array = context[context.Count - 1].Split('.');
                item.ItemNumber = int.Parse(array[0]);
                item.GoodsNumber = array[1];
                context.RemoveAt(context.Count - 1);
            }
            private static void ParseGoodsName(List<string> context, DeclarationItem item)
            {
                item.GoodsName = context[0].Trim();
                context.RemoveAt(0);
                item.GoodsSpecificationModel = string.Join(string.Empty, context);
                context.Clear();
            }
            private static void ParseDestinationCountryNumber(List<string> context, DeclarationItem item)
            {
                item.DestinationCountryNumber = context[context.Count - 1].TrimStart('(').TrimEnd(')').Trim();
                context.RemoveAt(context.Count - 1);
            }
            private static void ParseDestinationCountry(List<string> context, DeclarationItem item)
            {
                item.DestinationlCountry = context[context.Count - 1];
                context.RemoveAt(context.Count - 1);
            }
            private static void ParseCurrency(List<string> context, DeclarationItem item)
            {
                item.CurrencyName = context[context.Count - 1];
                context.RemoveAt(context.Count - 1);
            }

            private static void ParsPriceToTax(List<string> context, DeclarationItem item)
            {
                string[] array = context[context.Count - 1].Trim().Split(' ');
                item.GoodsPrice = decimal.Parse(array[0]);
                item.GoodsAmount = decimal.Parse(array[1]);
                item.Currency = array[2];
                item.Tax = array[3];
                context.RemoveAt(context.Count - 1);
            }
        }
    }
}